
import sys
from code import InteractiveInterpreter
import time


#The purpose of using killable threads is that, if the user types an infinite loop, there is a way to get out.
class ThreadInterpreter(InteractiveInterpreter):
	thread = None
	useThread = True
	busy = False
	locals = None
	def __init__(self, useThread=True, locals=None):
		if locals: self.locals = dict(locals)
		InteractiveInterpreter.__init__(self, self.locals)
		
	def write(self, data):
		# This method is still relevant because errors are redirected here. The "print" actually goes to the callback though.
		print data
		
	def eval(self, s):
		self.busy = True
		if self.useThread: return self.eval_thread(s)
		else: return self.eval_nothread(s)
		
	def eval_nothread(self, s):
		s = s+ '\n\n' # add two newlines to the string to be interpretted, to ensure that it isn't just waiting for more (for i in range(3):5)
		self.runsource(s)
		self.busy = False
	
	def eval_thread(self, s):
		#Any code run here takes a performance hit because of the trace.
		import kthread
		s = s+ '\n\n'
		
		def eval_thread_go(self, s):
			self.runsource(s)
			self.cleanup() #If the thread finishes without being interrupted, clean up
		
		self.thread = kthread.KThread(eval_thread_go, self, s)
		self.thread.start()
	
	def stop_thread(self):
		#~ self.wait(0.5) #Give time for execution to stop. It is not garunteed to stop instantly.
		self.thread.kill()
		print 'Execution stopped.' #Otherwise, the thread finished normally.
		self.cleanup()
		
	def cleanup(self):
		self.busy = False
	
	def wait(self, n):
		time.sleep(n)
		
	def clear_locals(self):
		self.locals = {}

	
class PseudoFile(object):
	def __init__(self, writefn, name='', encoding=None):
		self.writefn = writefn
		self.name = name
	def write(self, s):
		self.writefn(s)
	def writelines(self, l):
		map(self.write, l)
	def flush(self):
		pass
	def isatty(self):
		return True
		

	
if __name__=='__main__':
	import time
	
	def test(bThread):
		interp = ThreadInterpreter(useThread=bThread)
		basicTest = False
		if basicTest:
			interp.eval('2+2')
		else:
			interp.eval('for i in range(1000): print i')
			time.sleep(0.001)
			interp.stop_thread()
	def main():
		print "WITH Threads"
		test(True)
		#~ print "Without Threads"
		#~ test(False)
		
	main()